// ! 闭包

/// 闭包作为参数
// FnOnce(); 祖先级
// FnMut();  父 FnMut -> FnOnce
// Fn();     子 Fn -> FnMut -> FnOnce
fn closure_math<F: Fn() -> i32>(op: F) -> i32 {
  op()
}

/// 闭包作为返回值
fn two_times_impl() -> impl Fn(i32) -> i32 {
  let i = 2;
  // 此处使用了 move 关键字
  // 是因为 i 属于局部变量，当函数调用完毕 就会销毁，这是 i 就成了悬垂指针
  // 使用 move 将 i 的所有权转移出去 这才是正确的
  move |j| j * i
}

fn main() {
  //=================== 闭包的使用  ===================
  let out = 42;
  let mut mut_out = 0;
  //   报错 can't capture dynamic environment in a fn item
  //    use the `|| { ... }` closure form instead
  //    无法在fn项中捕获动态环境
  //    使用`|{。。。}`取而代之的是闭包形式
  // fn add(i: i32, j: i32) -> i32 {
  //     i + j + out
  // }

  // 函数
  fn add(i: i32, j: i32) -> i32 {
      i + j
  }

  // 闭包
  let closure_annotated = |i: i32, j: i32| -> i32 { i + j + out };
  let closure_inferred = |i, j| i + j + out;
  let mut closure_mut = |i: i32, j: i32| mut_out = i + j;
  let i = 1;
  let j = 2;
  assert_eq!(3, add(i, j));
  assert_eq!(45, closure_annotated(i, j));
  assert_eq!(45, closure_inferred(i, j));
  closure_mut(i, j);
  assert_eq!(3, mut_out);
  // 总结：函数不能捕获外部变量的值 (out) 而闭包可以捕获

  //=================== end (闭包的使用) ===================

  //=================== 闭包作为参数 ===================
  let a = 5;
  let b = 6;
  assert_eq!(closure_math(|| a + b), 11);
  assert_eq!(closure_math(|| a * b), 30);
  //=================== end (闭包作为参数) ===================

  //=================== 闭包作为返回值 ===================
  let result = two_times_impl();
  assert_eq!(10, result(5));
  //=================== end (闭包作为参数) ===================
}
